package nl.unimaas.games.tron.player.mcts;

import java.util.ArrayList;

import nl.unimaas.games.tron.engine.Board;
import nl.unimaas.games.tron.player.MCTSPlayer.Node;
import nl.unimaas.games.tron.player.MCTSSolverPlayer.SolverNode;
import nl.unimaas.games.tron.util.KnuthRandom;

public interface SolverSelectionStrategy {
	public SolverNode getNode(SolverNode node);
	
	public final static SolverSelectionStrategy RANDOM = new SolverSelectionStrategy() {
		@Override
		public String toString() {
			return "random";
		}

		@Override
		public SolverNode getNode(SolverNode node) {
			ArrayList<SolverNode> candidates = new ArrayList<SolverNode>();

			for (int i = 0; i < node.children.size(); i++) {
				SolverNode n = (SolverNode) node.children.get(i);
				//if (n.proven)
				//	continue;
				//pruning
				if (node.max) {
					if (n.pess < node.opti)
						candidates.add(n);
					else
						System.out.println("max skip");
				}
				else {
					if (n.opti > node.pess)
						candidates.add(n);
					else
						System.out.println("min skip");
				}
				//candidates.add(n);
			}
			if (candidates.isEmpty())
				return null;
			else
				return candidates.get(KnuthRandom.nextInt(candidates.size()));
		}
	};
	
	public final static SolverSelectionStrategy UCT = new UCT(10, 30);
	
	public final static SolverSelectionStrategy SIMPLE_UCT = new SolverSelectionStrategy() {
		private SelectionStrategy uct = new SelectionStrategy.UCT(10, 30);
		
		@Override
		public SolverNode getNode(SolverNode node) {
			return (SolverNode) uct.getNode(node);
		}
		
		@Override
		public String toString() {
			return "simpleUCT";
		}
		
	};
	
	public static class UCT implements SolverSelectionStrategy {
		protected double C = 10;
		protected int T = 30;
		
		public UCT(double C, int T) {
			this.C = C;
			this.T = T;
		}
		
		@Override
		public SolverNode getNode(SolverNode node) {
			SolverNode bestNode = null;
			double bestValue = Double.NEGATIVE_INFINITY;
			double uct;
			for (int i = 0; i < node.children.size(); i++) {
				SolverNode n = (SolverNode) node.children.get(i);
				if (n.proven)
					continue;
				//pruning
				if (node.max) {
					if (n.pess >= node.opti)
						continue;
				}
				else if (n.opti <= node.pess)
					continue;
				
				uct = n.value / (n.visits + 1.0) + C * Math.sqrt(Math.log(node.visits + 1.0) / (n.visits + 1.0));

	            if (uct > bestValue) {
	            	bestNode = n;
	                bestValue = uct;
	            }
			}
	        return bestNode;
		}
		
		@Override
		public String toString() {
			return "UCTSolve";
		}
	}
	
	public static class BiasedUCT implements SolverSelectionStrategy {
		protected double C = 10;
		protected int T = 30;
		protected double W = 10;
		
		public BiasedUCT(double C, int T, double W) {
			this.C = C;
			this.T = T;
			this.W = W;
		}
		
		@Override
		public SolverNode getNode(SolverNode node) {
			SolverNode bestNode = null;
			double bestValue = Double.NEGATIVE_INFINITY;
			double uct, pb;
			for (int i = 0; i < node.children.size(); i++) {
				SolverNode n = (SolverNode) node.children.get(i);
				if (n.proven)
					continue;
				//pruning
				if (node.max) {
					if (n.pess >= node.opti)
						continue;
				}
				else if (n.opti <= node.pess)
					continue;
				
				if (node.move != Board.MOVE_NONE)
					pb = (W * (Double) n.tag) / (n.visits - n.draws - n.wins + 1.0);
				else
					pb = 0;
				uct = n.value / (n.visits + 1.0) + C * Math.sqrt(Math.log(node.visits + 1.0) / (n.visits + 1.0)) + pb;

	            if (uct > bestValue) {
	            	bestNode = n;
	                bestValue = uct;
	            }
			}
	        return bestNode;
		}
		
		@Override
		public String toString() {
			return "BiasedUCTSolve";
		}
	}
}
